package com.company.project.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.company.project.constant.Constants;
import com.company.project.utils.MD5Utils;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 拦截器 验证签名 目前只支持GET/POST请求
 *
 * @author zangdaiyang
 */
@Component
@Slf4j
public class SignAuthInterceptor implements HandlerInterceptor {
    private final Logger logger = LoggerFactory.getLogger(SignAuthInterceptor.class);
    private final int expires = 120;//过期时间(秒)

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // options 预请求忽略
        if ("OPTIONS".equals(request.getMethod())) {
            return true;
        }
        String isGod = request.getHeader("isGod");
        if (!StringUtils.isEmpty(isGod) && isGod.equals("true")) {
            return true;
        }
        //忽略验证的 api
        if (request.getRequestURL().toString().contains("/api/public")) {
            return true;
        }
        // 1、获取请求的参数
        Map<String, String> params = null;
        String method = request.getMethod();
        String signKey = request.getHeader("sign");
        String timestamp = request.getHeader("timestamp");

        if (StringUtils.isEmpty(signKey) || StringUtils.isEmpty(timestamp)) {
            responseResult(response, "签名和时间戳不能为空");
            return false;
        }
        long time = (System.currentTimeMillis() - Long.parseLong(timestamp)) / 1000;
        if (time > expires) {
            responseResult(response, "请求已过期");
            return false;
        }
        if (Constants.HTTP_GET.equalsIgnoreCase(method)) {
            Map<String, String[]> paramsArr = request.getParameterMap();
            if (CollectionUtils.isEmpty(paramsArr)) {
                log.warn("Request for get method, param is empty, signature verification failed.");
                responseResult(response, "参数不能为空");
                return false;
            }
            params = convertParamsArr(paramsArr);
        } else if (Constants.HTTP_POST.equalsIgnoreCase(method)) {
            // 此处读取了request中的inputStream，因为只能被读取一次，后面spring框架无法读取了，所以需要添加wrapper和filter解决流只能读取一次的问题
            BufferedReader reader = request.getReader();
            if (reader == null) {
                log.warn("Request for post method, body is empty, signature verification failed.");
                responseResult(response, "参数不能为空");
                return false;
            }

            try {
                params = new Gson().fromJson(reader, Map.class);
            } catch (Exception e) {
                StringBuilder buffer = new StringBuilder();
                String line = " ";
                while ((line = reader.readLine()) != null) {
                    buffer.append(line);
                }
                e.printStackTrace();
                log.error("Gson 转换失败 : {} ", buffer.toString());
            }
        } else {
            log.warn("Not supporting non-get or non-post requests, signature verification failed.");
            return false;
        }

        // 2、验证签名是否匹配
        boolean checkSign = signKey.equals(MD5Utils.stringToMD5(params, timestamp));
        if (!checkSign) {
            log.info("Signature verification ok: {}, URI: {}, method: {}, params: {}", checkSign, request.getRequestURI(), method, params);
            responseResult(response, "验签失败");
        }
        return checkSign;
    }

    private Map<String, String> convertParamsArr(Map<String, String[]> paramsArr) {
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : paramsArr.entrySet()) {
            params.put(entry.getKey(), entry.getValue()[0]);
        }
        return params;
    }

    private void responseResult(HttpServletResponse response, String message) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);
        JSONObject res = new JSONObject();
        res.put("code", 400);
        res.put("message", message);
        try {
            response.getWriter().write(JSON.toJSONString(res));
        } catch (IOException ex) {
            logger.error(ex.getMessage());
        }
    }
}


